/*
 * inotify-sync - a simple file synchronizer and file system watcher
 * Copyright (C) 2010-2011, inotify-sync developers and inotify-sync contributors
 * Copyright (C) 2010-2011, Cohesion Network Security Studio
 *
 * inotify-sync is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * inotify-sync is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with inotify-sync; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */

#include "EventHandler.h"

EventHandler::EventHandler(Profile& profile)
{
	this->profile=profile;

	watch_dir = profile.get_popt("watch_dir");
	if(-1 == access(watch_dir.c_str(), R_OK|X_OK))
	{
		perror("access()");
		exit(errno);
	}

	// 同步目录如果不为空则必须有rwx的权限
	sync_dir = profile.get_popt("sync_dir");
	if(sync_dir != ""
	&& -1 == access(sync_dir.c_str(), R_OK|W_OK|X_OK))
	{
		perror("access()");
		exit(errno);
	}

	// 临时目录如果不为空则必须有rwx的权限
	temp_dir = profile.get_popt("temp_dir");
	if(temp_dir != ""
	&& -1 == access(temp_dir.c_str(), R_OK|W_OK|X_OK))
	{
		if(-1 == mkdir(temp_dir.c_str(), S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH))
		{
			perror("access()");
			exit(errno);
		}
	}

	log_enable = profile.get_popt("log_enable");

	common_action = profile.get_popt("common_action");
	if(common_action != ""
	&& -1 == access(common_action.c_str(), R_OK|X_OK))
	{
		perror("access()");
		exit(errno);
	}

	for_each_event_action()
	{
		if(Profile::event_action_array[i].action != ""
		&& -1 == access(Profile::event_action_array[i].action.c_str(), R_OK|X_OK))
		{
			perror("access()");
			exit(errno);
		}
	}
	
	custom_log = profile.get_popt("custom_log");
	if(-1 == access(custom_log.c_str(), R_OK|W_OK))
	{
		perror("access()");
		exit(errno);
	}

	error_log = profile.get_popt("error_log");
	if(-1 == access(error_log.c_str(), R_OK|W_OK))
	{
		perror("access()");
		exit(errno);
	}
}

EventHandler::~EventHandler()
{}

void
EventHandler::get_inotify(Inotify *p_inotify)
{
	this->p_inotify=p_inotify;
}

void
EventHandler::handle_events(std::vector<DirNode> *p_dnode_vec,
		const std::vector<inotify_event *> * const p_ievent_p_vec)
{
	for(size_t i=0;i<p_ievent_p_vec->size();i++)
	{
		// 取出当前事件指针
		inotify_event *p_ievent=p_ievent_p_vec->at(i);
		// 根据当前事件wd获得事件发生目录
		DirNode dnode;
		for(size_t j=0;j<p_dnode_vec->size();j++)
		{
			if(p_dnode_vec->at(j).wd==p_ievent->wd)
			{
				dnode=p_dnode_vec->at(j);
				break;
			}
		}
		// 事件发生目录
		string fname = dnode.abs_path;
		// 事件掩码
		const uint32_t mask = p_ievent->mask;
		// 事件cookie
		std::string cookie = Conv.conv<std::string>(p_ievent->cookie);
		// 如不为IN_DELETE_SELF或IN_MOVE_SELF事件，则绝对路径
		// 为：事件发生目录 + 文件名
		if(!(mask & IN_DELETE_SELF)
		&& !(mask & IN_MOVE_SELF))
		{
			fname.append("/");
			fname.append(p_ievent->name);
		}
		// 文件相对于监控根目录的路径
		std::string relative_path = fname;
		relative_path.replace(0,watch_dir.length(),"");


		// 对创建目录事件，初始化监控目录
		if((mask & IN_CREATE) && (mask & IN_ISDIR))
		{
			p_inotify->init_dir(fname);
		}
		// 对删除目录事件，撤销监控目录
		if((mask & IN_DELETE) && (mask & IN_ISDIR))
		{
			p_inotify->destory_dir(fname);
		}
		// 对移入目录事件，初始化监控目录
		if((mask & IN_MOVED_TO) && (mask & IN_ISDIR))
		{
			p_inotify->init_dir(fname);
		}
		// 对移出目录事件，初始化监控目录
		if((mask & IN_MOVED_FROM) && (mask & IN_ISDIR))
		{
			p_inotify->destory_dir(fname);
		}

		for_each_event_action()
		{
			if(mask & Profile::event_action_array[i].event)
			{
				exec_action(Profile::event_action_array[i].action,
						relative_path,
						fname,
						ISDIR(mask),
						cookie);
			}
		}
	}
}

inline void
EventHandler::exec_action(std::string action,
			std::string relative_path,
			std::string fname,
			const char *is_dir,
			std::string cookie)
{
	// 新开一个进程运行对应的action脚本
	pid_t pid;
	if((pid = fork()) < 0)
	{
		perror("fork()");
	}
	else if(pid == 0)
	{
		execl(action.c_str(),
			"",
			this->common_action.c_str(),
			this->watch_dir.c_str(),
			relative_path.c_str(),
			fname.c_str(),
			is_dir,
			this->sync_dir.c_str(),
			cookie.c_str(),
			this->temp_dir.c_str(),
			custom_log.c_str(),
			error_log.c_str(),
			log_enable.c_str(),
			(char *)0);
	}
	if(waitpid(pid,NULL,0) < 0)
	{
		perror("waitpid()");
	}
}
